package com.ccteam.fluidmusic.widget

import android.animation.*
import android.content.Context
import android.graphics.Color
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.support.v4.media.session.PlaybackStateCompat
import android.util.AttributeSet
import android.util.TypedValue
import android.view.*
import android.view.animation.DecelerateInterpolator
import android.view.animation.PathInterpolator
import android.widget.TextView
import androidx.core.view.marginTop
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.ccteam.fluidmusic.R
import com.ccteam.fluidmusic.databinding.LyricsBodyItemBinding
import com.ccteam.fluidmusic.databinding.LyricsStringItemBinding
import com.ccteam.fluidmusic.fluidmusic.media.extensions.currentPlaybackPosition
import com.ccteam.fluidmusic.fluidmusic.media.extensions.isOnlyPlaying
import com.ccteam.fluidmusic.fluidmusic.media.extensions.isPlaying
import com.ccteam.fluidmusic.view.adapters.ViewBindViewHolder
import com.ccteam.fluidmusic.view.adapters.viewbinding.MultiTypeViewBindingAdapter
import com.ccteam.fluidmusic.view.bean.LyricsBodyItem
import com.ccteam.fluidmusic.view.bean.LyricsBodyItem.Companion.HIGH_LIGHT_CHANGE
import com.ccteam.fluidmusic.view.bean.LyricsStringItem
import com.ccteam.shared.result.BaseItemData
import kotlinx.coroutines.*
import java.lang.ref.WeakReference

/**
 * @author Xiaoc
 * @since 2021/2/24
 *
 * 歌词RecyclerView封装
 * 歌词类型分为两种：
 * I：可自动滚动类，由 [LyricsBodyItem] 包装
 * II：不可自动滚动类，仅含文字描述
 *
 * 依据不同的类型展示不同的效果
 */
class LyricsRecyclerView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0) : RecyclerView(context,attrs, defStyleAttr), LifecycleObserver {

    companion object{
        /**
         * 当前播放位置更新发出的事件
         */
        private const val POSITION_EVENT = 20
    }

    // 协程组件
    private val autoPullJob = SupervisorJob()
    private val autoPullScope = CoroutineScope(Dispatchers.Main + autoPullJob)

    private var lyricsBodyList: List<LyricsBodyItem> = emptyList()

    /**
     * 当前自动滚动到的位置，初始为 -1
     */
    private var currentPosition: Int = -1

    /**
     * 记录最后一次更新position前的Position值，主要用于恢复和重建等操作
     */
    private var lastUpdatePosition: Int = -1

    /**
     * 减速插值器
     */
    private val decelerateInterpolator = DecelerateInterpolator(1.2f)

    /**
     * 播放状态暂存，如果播放状态发生变化，请随时调用 [updateState] 方法
     * @see updateState
     */
    private var playbackState: PlaybackStateCompat? = null

    /**
     * 当前是否是拖动状态或滚动状态，如果为true则不会进行自动滚动
     */
    private var isTouching = false

    /**
     * 自动执行处理Handler，用于自动滚动和自动返回等内容的处理
     */
    private val handler = AutoHandler(this)

    private var config: Config = Config(resources.getDimension(R.dimen.lyrics_defult_text_size))

    private val scrollListener = object: RecyclerView.OnScrollListener() {
        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            // 判断当前状态是否是没有被触摸且滑动的状态
            isTouching = SCROLL_STATE_IDLE != newState
        }
    }

    init {
        addOnScrollListener(scrollListener)
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        super.onLayout(changed, l, t, r, b)

        // 动态设置底部padding
        val screenHeight = height
        val paddingBottom = screenHeight - screenHeight / 5

        setPadding(0,0,0,paddingBottom)

    }

    fun setLifecycleOwner(lifecycleOwner: LifecycleOwner){
        lifecycleOwner.lifecycle.addObserver(this)
    }

    /**
     * 设置歌词适配器
     */
    fun setLyricsAdapter(callback: LyricsItemClickCallback){
        adapter = BaseLyricsAdapter(callback,config)
    }

    fun setConfig(config: Config){
        this.config = config
        getLyricsAdapter()?.updateConfig(config)
    }

    /**
     * 得到Adapter适配器
     */
    fun getLyricsAdapter(): BaseLyricsAdapter?{
        return adapter as? BaseLyricsAdapter
    }

    /**
     * 更新当前播放状态
     * 根据当前播放状态进行对应内容的处理
     * 例如如果是正在播放状态，则开始更新当前位置并进行自动滚动
     * 如果是暂停状态，则不进行任何处理
     */
    fun updateState(playbackStateCompat: PlaybackStateCompat){
        playbackState = playbackStateCompat
        handler.removeMessages(POSITION_EVENT)
        if(playbackStateCompat.isOnlyPlaying){
            updatePosition(playbackStateCompat.currentPlaybackPosition)
        }
    }

    /**
     * 更新进度
     * 传入当前所播放的时间进度(ms)，会自动计算当前时间所处的对应位置
     * 做出相应处理：
     * 如果当前歌词位置和高亮位置不相同，则进行高亮恢复滑动等操作
     */
    private fun updatePosition(currentTime: Long){
        autoPullScope.launch {
            val position = getCurrentPosition(currentTime)
            if(position < 0){
                return@launch
            }
            // 如果当前位置和需要重新跳转的位置相同，则不进行对应滚动跳转，只有不相同才进行跳转
            if(currentPosition != position){
                lastUpdatePosition = currentPosition
                currentPosition = position
            }

            changeHighLight(position,lastUpdatePosition,currentTime)
        }
    }

    /**
     * 改变高亮状态并开启自动滚动
     * @param position 当前要跳转的position
     * @param lastUpdatePosition 上一个更新的position位置
     * @param currentTime 当前时间戳
     */
    private fun changeHighLight(position: Int,
                                lastUpdatePosition: Int,
                                currentTime: Long){
        val nextPosition = if(position + 1 < lyricsBodyList.size){
            position + 1
        } else {
            position
        }
        // 恢复当前高亮状态
        getLyricsAdapter()?.changeHighLine(lastUpdatePosition,position)

        try {
            // 发送下一次更新歌词内容的事件
            handler.sendEmptyMessageDelayed(
                POSITION_EVENT,
                lyricsBodyList[nextPosition].startTime
                        - currentTime)
        } catch (e: IndexOutOfBoundsException){

        }
    }


    /**
     * 更新文本颜色
     * 当文字颜色发生变化时可以调用
     *
     * @param normalColor 正常字体颜色
     * @param highlightColor 高亮字体颜色
     */
    fun updateTextColor(normalColor: Int,highlightColor: Int){
        getLyricsAdapter()?.updateTextColor(normalColor,highlightColor)
    }

    /**
     * 设置歌词列表
     * 歌词列表有多种类型，目前暂支持时间轴类型，后续会增加歌手信息类型
     * @param list 歌词列表
     * @param canScroll 是否为滚动歌词
     */
    fun submitLyricsList(list: List<BaseItemData>,canScroll: Boolean){
        autoPullScope.launch {
            getLyricsAdapter()?.let {
                if(canScroll){
                    val bodyList = list.filterIsInstance<LyricsBodyItem>()
                    lyricsBodyList = bodyList

                    playbackState?.let { state ->
                        updateState(state)
                    }

                    it.submitList(bodyList){
                        // 当列表更新完毕后，进行跳转到当前行的步骤
                        toCurrentLine()
                    }
                } else {
                    val bodyList = list.filterIsInstance<LyricsStringItem>()
                    it.submitList(bodyList)
                }
            }
        }
    }

    /**
     * 跳转到当前播放行
     * 依靠 [currentPosition] 进行径直跳转
     */
    private fun toCurrentLine(){
        val manager = layoutManager as? LinearLayoutManager
        if(!lyricsBodyList.isNullOrEmpty() && !isTouching && manager != null){
            if(manager.findViewByPosition(currentPosition) == null){
                manager.scrollToPositionWithOffset(currentPosition,150)
            }
        }
    }

    /**
     * 得到当前时间轴所处的position的位置
     * 如果不存在则返回 -1
     * @param currentTime 当前时间轴(ms)
     */
    private suspend fun getCurrentPosition(currentTime: Long): Int{
        return withContext(Dispatchers.Default){
            if(lyricsBodyList.isNullOrEmpty()){
                return@withContext -1
            }
            for(index in lyricsBodyList.indices){
                if(currentTime <= lyricsBodyList[index].startTime){
                    return@withContext if(index == 0) 0 else index - 1
                }
            }
            return@withContext lyricsBodyList.size - 1
        }
    }

    /**
     * 自动处理Handler执行器
     */
    private class AutoHandler(
        lyricsRecyclerView: LyricsRecyclerView
    ): Handler(Looper.getMainLooper()){

        private val recyclerView = WeakReference(lyricsRecyclerView)

        override fun handleMessage(msg: Message) {
            when(msg.what){
                POSITION_EVENT ->{
                    handleAutoPull()
                }
            }
        }

        /**
         * 处理自动滚动
         * 目前自动滚动采用 [smoothScrollBy] 方法进行滚动
         * 自动滚动会做如下处理：
         * 1.改变高亮位置
         * 2.如果不处于触摸状态且当前视图中有该位置信息，则进行滚动，将高亮滚动到中顶部位置
         * 3.计算下一次要执行该任务的时长，进行延期执行
         * 4.如果处于触摸状态，则仅更新高亮位置而不进行滚动，同样如果当前视图没有该位置信息，也不进行滚动
         */
        private fun handleAutoPull(){
            removeMessages(POSITION_EVENT)
            recyclerView.get()?.let { rv ->
                rv.getLyricsAdapter()?.let { adapter ->
                    if(rv.lyricsBodyList.size > rv.currentPosition + 1){
                        rv.currentPosition ++
                        val current = rv.currentPosition
                        val nextPosition = current + 1

                        // 通知Adapter改变高亮位置
                        adapter.changeHighLine(current - 1,current)
                        (rv.layoutManager as? LinearLayoutManager)?.let { manager ->
                            manager.findViewByPosition(current)?.let { out ->
                                // 当处于未触摸的情况下，自动滚动到高亮部分，通过得到对应位置的top距离 - 它的5倍margin（显示出上面歌词的一部分）
                                if(!rv.isTouching){
                                    rv.smoothScrollBy(0,out.top - out.marginTop * 5, rv.decelerateInterpolator,650)
                                }
                            } ?: run {
                                // 如果在当前视图没有找到该歌词，则使用manager进行滑动到那个位置稍微偏移处
                                // 这样能保证滑动的高亮位置在屏幕上方而不是下方
                                if(!rv.isTouching){
                                    manager.scrollToPositionWithOffset(current,150)
                                }
                            }
                        }
                        // 如果仍然存在下一项要滚动的内容，则将当前position++，否则说明播放到最后了，不进行滚动任务了
                        if(rv.lyricsBodyList.size > nextPosition){
                            if(rv.playbackState?.isPlaying == true){
                                sendEmptyMessageDelayed(
                                    POSITION_EVENT,rv.lyricsBodyList[nextPosition].startTime
                                        - (rv.playbackState?.currentPlaybackPosition ?: 0L))
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 重写这三个方法，防止requiresFadingEdge渐变边缘属性影响padding的相关效果
     */
    override fun isPaddingOffsetRequired(): Boolean {
        return true
    }

    override fun getTopPaddingOffset(): Int {
        return -paddingTop
    }

    override fun getBottomPaddingOffset(): Int {
        return paddingBottom
    }

    /**
     * 歌词适配器
     */
    class BaseLyricsAdapter(
        private val callback: LyricsItemClickCallback,
        private var config: Config
    ): MultiTypeViewBindingAdapter(LyricsBodyItem.differCallback){

        /**
         * 位置缓存，存储上一次位置和当前位置
         */
        private var positionCache: Pair<Int,Int> = Pair(-1,-1)

        /**
         * 文本颜色，默认颜色为黑色
         */
        private var textColor: Pair<Int,Int> = Pair(Color.parseColor(
            "#2e000000"),Color.parseColor("#f0000000"))

        /**
         * ARGB动画差值器
         */
        private val argbInterpolator = PathInterpolator(0.39F, 0.575F, 0.565F, 1.0F)

        /**
         * 缩放动画差值器
         */
        private val scaleInterpolator = PathInterpolator(0.4F, 0.1F, 0.0F, 1.0F)

        /**
         * 缩放到高亮的动画对象，复用
         */
        private val toHighlightScaleAnimator = ObjectAnimator().apply {
            interpolator = scaleInterpolator
            setValues(PropertyValuesHolder.ofFloat(View.SCALE_X, 0.95f,1f),
                PropertyValuesHolder.ofFloat(View.SCALE_Y, 0.95f,1f))
            startDelay = 50L
            duration = 500L
        }

        /**
         * 缩放到正常的动画对象，复用
         */
        private val toNormalScaleAnimator = ObjectAnimator().apply {
            interpolator = scaleInterpolator
            setValues(PropertyValuesHolder.ofFloat(View.SCALE_X, 1f,0.95f),
                PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f,0.95f))
            startDelay = 100L
            duration = 350L
        }

        override fun createMultiViewBinding(parent: ViewGroup, viewType: Int): ViewHolder? {
            return when(viewType){
                BaseItemData.LAYOUT_LYRICS_BODY_ITEM ->{
                    LyricsBodyViewHolder(callback,LyricsBodyItemBinding.inflate(LayoutInflater.from(parent.context),parent,false))
                }
                BaseItemData.LAYOUT_LYRICS_STRING_ITEM ->{
                    LyricsStringViewHolder(LyricsStringItemBinding.inflate(LayoutInflater.from(parent.context),parent,false))
                }
                else -> {
                    null
                }
            }
        }


        override fun bindMultiItem(
            holder: ViewHolder,
            position: Int,
            item: BaseItemData,
            payloads: MutableList<Any>,
            viewType: Int
        ) {
            var fullRefresh = payloads.isEmpty()

            when(viewType){
                BaseItemData.LAYOUT_LYRICS_BODY_ITEM ->{
                    if(payloads.isNotEmpty()){
                        payloads.forEach { payload ->
                            when(payload){
                                // 如果为高亮改变，则只改变高亮部分
                                HIGH_LIGHT_CHANGE ->
                                    handleHighLightChanged(holder as LyricsBodyViewHolder,position)
                                else ->
                                    fullRefresh = true
                            }
                        }
                    }

                    if(fullRefresh){
                        handleLyricsBodyItem(holder as LyricsBodyViewHolder,item as LyricsBodyItem,position)
                    }
                }
                BaseItemData.LAYOUT_LYRICS_STRING_ITEM ->{
                    handleLyricsStringItem(holder as LyricsStringViewHolder,item as LyricsStringItem)
                }
            }
        }


        /**
         * 改变高亮位置
         * 使用 notifyItemChanged 进行单项更新
         * @param previousPosition 上一个高亮位置
         * @param currentPosition 下一个要高亮的位置
         */
        fun changeHighLine(previousPosition: Int,currentPosition: Int){
            positionCache = Pair(previousPosition,currentPosition)
            notifyItemChanged(previousPosition,HIGH_LIGHT_CHANGE)
            notifyItemChanged(currentPosition,HIGH_LIGHT_CHANGE)
        }

        /**
         * 更改文本颜色（当为取色逻辑时会经常用）
         * @param normalColor 正常颜色
         * @param highlightColor 高亮颜色
         */
        fun updateTextColor(normalColor: Int,highlightColor: Int){
            textColor = Pair(normalColor,highlightColor)
            notifyDataSetChanged()
        }

        fun updateConfig(config: Config){
            this.config = config
            notifyDataSetChanged()
        }

        /**
         * 处理高亮改变
         */
        private fun handleHighLightChanged(holder: LyricsBodyViewHolder, position: Int){
            val (_,currentPosition) = positionCache
            val (normalColor,highlightColor) = textColor
            // 判断当前位置，如果与更改的position相同，说明是要变为高亮状态，否则变为正常状态
            when (currentPosition) {
                position -> {
                    // 如果已经处于高亮状态，则不做处理
                    if(holder.binding.rvLyricsBody.scaleX == 1f){
                        return
                    }
                    val valueAnimator = ValueAnimator.ofArgb(normalColor,highlightColor)
                    valueAnimator.startDelay = 50L
                    valueAnimator.duration = 250L
                    valueAnimator.interpolator = argbInterpolator
                    valueAnimator.addUpdateListener(AlphaAnimatorListener(holder.binding.rvLyricsBody))

                    toHighlightScaleAnimator.target = holder.binding.rvLyricsBody
                    val animator = AnimatorSet()
                    animator.playTogether(valueAnimator,toHighlightScaleAnimator)
                    animator.start()
                }
                else -> {
                    // 如果已经处于正常状态，则不做处理
                    if(holder.binding.rvLyricsBody.scaleX == 0.95f){
                        return
                    }
                    val valueAnimator = ValueAnimator.ofArgb(highlightColor,normalColor)
                    valueAnimator.startDelay = 250L
                    valueAnimator.duration = 350L
                    valueAnimator.interpolator = argbInterpolator
                    valueAnimator.addUpdateListener(AlphaAnimatorListener(holder.binding.rvLyricsBody))

                    toNormalScaleAnimator.target = holder.binding.rvLyricsBody
                    val animator = AnimatorSet()
                    animator.playTogether(valueAnimator,toNormalScaleAnimator)
                    animator.start()
                }
            }
        }

        /**
         * 完整处理歌词内容
         */
        private fun handleLyricsBodyItem(holder: LyricsBodyViewHolder, item: LyricsBodyItem, position: Int){
            holder.item = item
            holder.binding.rvLyricsBody.text = item.lyricText
            holder.binding.rvLyricsBody.setTextSize(TypedValue.COMPLEX_UNIT_PX,config.lyricTextSize)
            // 设置缩放中心为TextView的高度
            holder.binding.rvLyricsBody.viewTreeObserver.addOnPreDrawListener(
                ViewTreeListener(holder.binding.rvLyricsBody)
            )

            val (_,currentPosition) = positionCache
            val (normalColor,highlightColor) = textColor
            if(currentPosition == position){
                holder.binding.rvLyricsBody.scaleX = 1f
                holder.binding.rvLyricsBody.scaleY = 1f
                holder.binding.rvLyricsBody.setTextColor(highlightColor)
            } else {
                holder.binding.rvLyricsBody.scaleX = 0.95f
                holder.binding.rvLyricsBody.scaleY = 0.95f
                holder.binding.rvLyricsBody.setTextColor(normalColor)
            }
        }

        /**
         * 完整处理歌词内容
         */
        private fun handleLyricsStringItem(holder: LyricsStringViewHolder, item: LyricsStringItem){
            val (_,highlightColor) = textColor
            holder.binding.rvLyricsBody.text = item.lyricText
            holder.binding.rvLyricsBody.setTextColor(highlightColor)
        }

        /**
         * 由于在进行View绘制时不是每次获得height就是View绘制后的高度
         * 需要在View绘制时去获得，才能获得View真正的高度，才能正确设置缩放Y坐标
         */
        private class ViewTreeListener(
            view: View
        ): ViewTreeObserver.OnPreDrawListener{
            private val view = WeakReference(view)

            override fun onPreDraw(): Boolean {
                // 移除本次监听
                view.get()?.viewTreeObserver?.removeOnPreDrawListener(this)
                // 设置view的缩放Y坐标为自身底部
                view.get()?.pivotY = view.get()?.measuredHeight?.toFloat() ?: 0f
                return true
            }
        }

        /**
         * 透明度动画更新监听器
         * 会在更新透明度时动态更新文字颜色
         */
        private class AlphaAnimatorListener(
            textView: TextView
        ): ValueAnimator.AnimatorUpdateListener{
            private val view = WeakReference(textView)
            override fun onAnimationUpdate(animation: ValueAnimator) {
                view.get()?.setTextColor(animation.animatedValue as Int)
            }

        }

        inner class LyricsBodyViewHolder(
            callback: LyricsItemClickCallback,
            binding: LyricsBodyItemBinding
        ): ViewBindViewHolder<LyricsBodyItemBinding>(binding){

            var item: LyricsBodyItem? = null

            init {
                binding.rvLyricsBody.pivotX = 0f

                binding.root.setOnClickListener {
                    item?.let {
                        callback.lyricsItemClick(it)
                    }
                }
            }
        }

        inner class LyricsStringViewHolder(
            binding: LyricsStringItemBinding
        ): ViewBindViewHolder<LyricsStringItemBinding>(binding)
    }

    interface LyricsItemClickCallback{
        fun lyricsItemClick(lyricsItem: LyricsBodyItem)
    }

    data class Config(
        val lyricTextSize: Float
    )

    /**
     * 当被清理时，关闭监听器和歌词滚动等内容
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun clearView(){
        clearOnScrollListeners()
        autoPullScope.cancel()
        handler.removeMessages(POSITION_EVENT)
    }

    /**
     * 当恢复视图后，重新进行滚动等内容操作
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resumeView(){
        autoPullScope.launch {
            // 延迟1s后进行更新进度
            delay(1000L)
            playbackState?.let {
                updatePosition(it.currentPlaybackPosition)
                toCurrentLine()
            }
        }
    }

    /**
     * 当暂停视图后，重新进行滚动等内容操作
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun pauseView(){
        handler.removeMessages(POSITION_EVENT)
    }

}